package com.github.aloxc.plugin.restplus.component;

import com.github.aloxc.plugin.restplus.entity.HistoryTreeNode;
import com.github.aloxc.plugin.restplus.entity.RestConfig;
import com.github.aloxc.plugin.restplus.entity.SwaggerConfig;
import com.github.aloxc.plugin.restplus.listener.HistoryTreeSelectionListener;
import com.github.aloxc.plugin.restplus.navigation.action.RestServiceItem;
import com.github.aloxc.plugin.restplus.navigator.RestServiceDetail;
import com.github.aloxc.plugin.restplus.navigator.RestServiceProject;
import com.github.aloxc.plugin.restplus.navigator.RestServiceStructure;
import com.github.aloxc.plugin.restplus.utils.Md5Util;
import com.github.aloxc.plugin.restplus.utils.ProjectUtil;
import com.github.aloxc.plugin.restplus.utils.TreeUtil;
import com.intellij.openapi.editor.colors.FontPreferences;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.components.JBPanel;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.tree.*;
import java.awt.*;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

import static com.github.aloxc.plugin.restplus.common.Consts.HISTORY_FILE_EXTEND_NAME;
import static com.github.aloxc.plugin.restplus.common.Consts.LINE_NUMBER_BACKGROUND_COLOR;
import static javax.swing.ScrollPaneConstants.*;
import static javax.swing.tree.TreeSelectionModel.SINGLE_TREE_SELECTION;

/**
 *
 * @author leero
 */
public class HistoryForm extends JBPanel {
    private JTree historyTree;
    private JPanel histroyPanel;
    private JScrollPane historyListScroll;
    private JPanel rootPanel;
    private JScrollPane historyDetailScroll;

    private String url;
    private Project project;
    private RestServiceDetail restServiceDetail;
    private TextPane historyDetailTextPane;
    private int historyHeight = 600;
    private boolean allHistory;
    private String parentName;


    public HistoryForm(RestServiceDetail restServiceDetail, boolean allHistory,String parentName) {
        super();
        this.restServiceDetail = restServiceDetail;
        this.project = restServiceDetail.getProject();
        this.url = restServiceDetail.getUrl();
        this.allHistory = allHistory;
        this.parentName = parentName;
        initComponent();
    }

    private static ThreadLocal<SimpleDateFormat> SIMPLE_DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        }
    };


    public void initComponent() {
        initUi();
        SimpleAttributeSet keyAttr = new SimpleAttributeSet();
        StyleConstants.setFontSize(keyAttr, 14);
        initTreeData();
        initActions();

    }

    private void initActions() {
        bindHistoryTreeListener();

    }

    public String getUrl() {
        return url;
    }

    public Project getProject() {
        return project;
    }

    public void setProject(Project Project) {
        this.project = Project;
    }


    private void bindHistoryTreeListener() {
        historyTree.addTreeSelectionListener(new HistoryTreeSelectionListener(project, historyDetailTextPane));
    }

    @Override
    protected void printComponent(Graphics g) {
        super.printComponent(g);
    }

    private void initUi() {
        GridLayoutManager historyLayout = new GridLayoutManager(1, 2);
        historyLayout.setHGap(1);
        historyLayout.setVGap(1);
        historyLayout.setMargin(new Insets(20, 20, 20, 20));
        histroyPanel = new JBPanel<>();
        histroyPanel.setLayout(historyLayout);
        histroyPanel.setMaximumSize(new Dimension(-1, historyHeight));
        histroyPanel.setMinimumSize(new Dimension(-1, historyHeight));
        histroyPanel.setPreferredSize(new Dimension(-1, historyHeight));
        this.setBorder(BorderFactory.createEmptyBorder());
        this.setLayout(new GridLayoutManager(1, 1));


        initHistoryListPanel();
        initHistoryDetailPanel();
        initHistoryPanel();
    }

    private void initHistoryPanel() {

        this.add(histroyPanel,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        new Dimension(-1, historyHeight), new Dimension(-1, historyHeight), new Dimension(-1, historyHeight)));
    }

    private void initHistoryDetailPanel() {

        ScrollPaneLayout historyDetailScrollLayout = new ScrollPaneLayout();
        historyDetailScrollLayout.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
        historyDetailScrollLayout.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
        historyDetailScroll.setLayout(historyDetailScrollLayout);
        historyDetailTextPane = new TextPane(LINE_NUMBER_BACKGROUND_COLOR,project);
        Font font = getEffectiveFont();
        historyDetailTextPane.setShowLineNumber(true);
        historyDetailTextPane.setText("");
        historyDetailTextPane.setFont(font);
        historyDetailTextPane.format();

        historyDetailScroll.getViewport().add(historyDetailTextPane,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_SOUTHEAST, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_FIXED,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1)));
        histroyPanel.add(historyDetailScroll,
                new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_SOUTHEAST, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        GridConstraints.FILL_NONE,
                        new Dimension(-1, -1), new Dimension(-1, -1), new Dimension(-1, -1)));
    }

    private void initHistoryListPanel() {
        int historyWidth = 250;
        ScrollPaneLayout historyListScrollLayout = new ScrollPaneLayout();
        historyListScrollLayout.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
        historyListScrollLayout.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
        historyListScroll.setLayout(historyListScrollLayout);
        historyListScroll.setPreferredSize(new Dimension(historyWidth, historyHeight));
        historyTree.setBackground(this.getBackground());
        historyListScroll.getViewport().add(historyTree,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_SOUTHEAST, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_FIXED,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        new Dimension(historyWidth, -1), new Dimension(historyWidth, -1), new Dimension(historyWidth, -1)));
        histroyPanel.add(historyListScroll,
                new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_SOUTHEAST, GridConstraints.FILL_BOTH,
                        GridConstraints.SIZEPOLICY_FIXED,
                        GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                        new Dimension(historyWidth, -1), new Dimension(historyWidth, -1), new Dimension(historyWidth, -1)));

    }

    private void initTreeData() {
        DefaultTreeModel model = (DefaultTreeModel) historyTree.getModel();
        final DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
        root.removeAllChildren();
        if (allHistory) {
            //处理全局的历史记录
            String rootPath = ProjectUtil.getHistoryStorePath(project);
            File rootFile = new File(rootPath);
            File[] rootPathList = rootFile.listFiles();
            if (rootPathList == null) {
                model.reload(root);
                historyTree.expandPath(new TreePath(root));
                historyTree.updateUI();
                return;
            } else {
                final Map<String, File[]> serviceMap = Arrays.stream(rootPathList).collect(Collectors.toMap(k -> k.getName(), v -> v.listFiles()));
                List<RestServiceProject> restServiceProjectList = RestServiceStructure.getRestServiceProjectList(project);
                List<RestServiceProject> restServiceProjectList1 = new ArrayList<>();
                restServiceProjectList.stream().forEach(it->{
                    List<RestServiceItem> serviceItems = it.getServiceItems();
                    Map<String,RestServiceItem> disMap = new HashMap<>(2<<3);
                    serviceItems.stream().forEach(service->{
                        disMap.put(service.getUrl(),service);
                    });
                    List<RestServiceItem> itemList = new ArrayList<>();
                    for(RestServiceItem item : disMap.values()){
                        itemList.add(item);
                    }
                    RestServiceProject p = new RestServiceProject(it.getName(),itemList);
                    restServiceProjectList1.add(p);
                });
                restServiceProjectList1.stream().forEach(it -> {
                    final HistoryTreeNode moduleNode = new HistoryTreeNode(it.getName(), it.getName(), null,null);
                    it.getServiceItems().stream().forEach(service -> {
                        String url = service.getUrl();
                        Module module = service.getModule();
                        String path = Md5Util.encriypt(module.getName() + "/" + url);
                        if (serviceMap.containsKey(path)) {
                            File[] files = serviceMap.get(path);
                            HistoryTreeNode urlNode = new HistoryTreeNode(url, module.getName(), null,null);
                            for (File f : files) {
                                String time = SIMPLE_DATE_FORMAT.get().format(new Date(Long.parseLong(f.getName().replaceAll(HISTORY_FILE_EXTEND_NAME, ""))));
                                HistoryTreeNode node = new HistoryTreeNode(time, module.getName(), f.getPath(),null);
                                urlNode.add(node);
                            }
                            if (urlNode.getChildCount() > 0) {
                                moduleNode.add(urlNode);
                            }
                        }
                    });
                    if (moduleNode.getChildCount() > 0) {
                        root.add(moduleNode);
                    }
                });

                RestConfig restConfig = RestConfig.loadConfig(project);
                List<SwaggerConfig> swaggerConfigList = restConfig.getSwaggerConfigList();
                swaggerConfigList.stream().forEach(swaggerConfig -> {
                    final HistoryTreeNode moduleNode = new HistoryTreeNode(swaggerConfig.getName(), swaggerConfig.getName(), null,swaggerConfig.getName());
                    swaggerConfig.getSwaggerRequestList().stream().forEach(service -> {
                        String url = service.getUrl();
                        String path = Md5Util.encriypt(swaggerConfig.getName() + "/" + url);
                        if (serviceMap.containsKey(path)) {
                            File[] files = serviceMap.get(path);
                            HistoryTreeNode urlNode = new HistoryTreeNode(url, null, null,swaggerConfig.getName());
                            for (File f : files) {
                                String time = SIMPLE_DATE_FORMAT.get().format(new Date(Long.parseLong(f.getName().replaceAll(HISTORY_FILE_EXTEND_NAME, ""))));
                                HistoryTreeNode node = new HistoryTreeNode(time, null, f.getPath(),swaggerConfig.getName());
                                urlNode.add(node);
                            }
                            if (urlNode.getChildCount() > 0) {
                                moduleNode.add(urlNode);
                            }
                        }
                    });
                    if (moduleNode.getChildCount() > 0) {
                        root.add(moduleNode);
                    }
                });

            }
        } else {
            //处理url的历史记录
            String servicePath = ProjectUtil.getHistoryStorePath(project, restServiceDetail.getModule(),parentName, url);
            VirtualFile path = LocalFileSystem.getInstance().findFileByPath(servicePath);
            if (path == null) {
                model.reload(root);
                historyTree.expandPath(new TreePath(root));
                historyTree.updateUI();
                return;
            }
            path.refresh(true, false);
            VirtualFile[] children = path.getChildren();
            Arrays.stream(children).forEach(it -> {
                String time = SIMPLE_DATE_FORMAT.get().format(new Date(Long.parseLong(it.getName().replaceAll(HISTORY_FILE_EXTEND_NAME, ""))));
                HistoryTreeNode node = new HistoryTreeNode(time, parentName == null ? restServiceDetail.getModule().getName() : null, it.getPath(),parentName);
                root.add(node);
            });
        }

        model.reload(root);

        TreePath rootPath = new TreePath(root);
        historyTree.expandPath(rootPath);
        TreeUtil.expandAll(historyTree, rootPath, true);
        historyTree.updateUI();
        //不显示根节点
        historyTree.setRootVisible(false);
        historyTree.setShowsRootHandles(false);
        historyTree.setRowHeight(22);
        DefaultTreeSelectionModel singleSelectionModel = new DefaultTreeSelectionModel();
        singleSelectionModel.setSelectionMode(SINGLE_TREE_SELECTION);

        historyTree.setSelectionModel(singleSelectionModel);
        DefaultTreeCellRenderer render = (DefaultTreeCellRenderer) (historyTree.getCellRenderer());
        render.setIcon(null);
        render.setLeafIcon(null);
        render.setOpenIcon(null);
        render.setClosedIcon(null);
    }


    @NotNull
    private Font getEffectiveFont(String text) {
        FontPreferences fontPreferences = this.getFontPreferences();
        List<String> effectiveFontFamilies = fontPreferences.getEffectiveFontFamilies();
        int size = fontPreferences.getSize(fontPreferences.getFontFamily());
        Font font = new Font(FontPreferences.DEFAULT_FONT_NAME, Font.PLAIN, size);
        for (String effectiveFontFamily : effectiveFontFamilies) {
            Font effectiveFont = new Font(effectiveFontFamily, Font.PLAIN, size);
            if (effectiveFont.canDisplayUpTo(text) == -1) {
                font = effectiveFont;
                break;
            }
        }
        return font;
    }

    @NotNull
    private final FontPreferences getFontPreferences() {
        return new FontPreferences();
    }

    @NotNull
    private Font getEffectiveFont() {
        int size = 14;
        return new Font("Monospaced", Font.PLAIN, size);
    }

    public void setUrl(String url) {
        this.url = url;
    }
}